package main

import (
	"fmt"
	"log"
	"os"
	"path"
	"encoding/binary"
)

// from: http://ulisse.elettra.trieste.it/services/doc/dbase/DBFstruct.htm
type tableHeader struct {
	Versioning byte
	LastUpdate [3]byte
	NRecs uint32
	HeaderSize uint16
	RecSize uint16 // record size
	DoesntMatter [20]byte
}

type fieldDescriptor struct {
	Name [11]byte
	Type [1]byte
	FieldDataMemAddress uint32 //irrelevant; just a placeholder
	FieldSize uint8
	FieldId uint8
	DoesntMatter [14]byte
}

func main() {
	if len(os.Args) != 2 {
		fmt.Println("Given a dbf file, return info about the records schema.")
		fmt.Println("Usage:", path.Base(os.Args[0]), "file.dbf")
		fmt.Println("The output format have one field per line, in the format:")
		fmt.Println("FIELDNAME start_position size")
		os.Exit(1)
	}
	var th tableHeader
	var fd fieldDescriptor
	f, err := os.Open(os.Args[1])
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()
	err = binary.Read(f, binary.LittleEndian, &th)
	if err != nil {
		log.Fatal(err)
	}
	// 32 bytes is the size of the header
	// and also the size for each field descriptor
	numFields := (th.HeaderSize-32)/32 
	// the first byte of every dbf record is the "deleted"
	// flag. when it has a *, it means the record is deleted.
	// otherwise it will contain a space.
	fmt.Println("deleted", 1, 1)
	var start uint32
	start = 2
	for i := numFields; i>0; i-- {
		err = binary.Read(f, binary.LittleEndian, &fd)
		if err != nil {
			log.Fatal(err)
		}
		fmt.Println(string(fd.Name[:]), start, fd.FieldSize)
		start = start + uint32(fd.FieldSize)
	}
}
